/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * tameshi
 * Copyright (C) Massimiliano Maniscalco 2012 <massi.neptune@yahoo.com>
 * 
 * tameshi is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * tameshi is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _DOMANDA_H_
#define _DOMANDA_H_

#define DA_IGNORARE 29
#define APOSTROFI   6
#define PUNTI		6

#include <gtkmm.h>
#include "tag_text.h"
#include <cassert>
#include <iostream>
#include <iomanip>
#include <time.h>
#include <set>

using namespace std;

static const Glib::ustring daIgnorare[DA_IGNORARE] = { "e", "o", "di", "a", "da", "in", "con", "su", "per", "tra", "fra", "il", "lo", "la", "i", "le", "un", "uno", "una", "dello", "della", "degli", "delle", "dei", "dallo", "dalla", "dalle", "dagli", "dai" };
static const Glib::ustring apostrofi[APOSTROFI] = { "l", "un", "d", "dell", "dall", "all" };
static const Glib::ustring punti[PUNTI] = { ".", ",", ":", ";", "!", "?" };

class Domanda
{
	
public:
	Domanda() :
		_codice ( "" ), _domanda ( "" ), _corretta ( -1 ), _argomento ( "" ), _appunti ( "" ), _risposte ( 0 ), _corrette ( 0 ), _ultimaIsCorretta ( false ), _tempoTotale ( 0 ), _ultimoTempo ( 0 ), _duplicato ( false )
	{
		for (unsigned char i = 0; i < _numeroRisposte; i++)
		{
			TagText tt = TagText ();
			_risposta.insert ( pair<unsigned char, TagText> (i, tt) );
		}
	}
	inline Domanda ( const Domanda &quaestio );
	inline ~Domanda ()
	{}
	inline Domanda& operator=( const Domanda &quaestio );
	inline bool operator<( const Domanda &quaestio ) const;
	inline bool operator==( const Domanda &quaestio ) const;
	inline void set_codice ( const Glib::ustring &str ) { _codice.assign ( str ); }
	inline void set_codice ( const char *cstr );
	inline Glib::ustring get_codice ( void ) const { return _codice; }
	inline void set_domanda ( const Glib::ustring &str ) { _domanda.set_stringa ( str ); }
	inline void set_domanda ( const char *cstr );
	inline void set_domanda ( Glib::RefPtr<Gtk::TextBuffer> txtbfr ) { _domanda.set_testo ( txtbfr ); }
	inline void append_domanda ( const Glib::ustring &str ) { _domanda.append_stringa ( str ); }
	inline void set_tags_domanda ( const list<Glib::ustring::size_type> &ttl ) { _domanda.set_tags ( ttl ); }
	inline Glib::ustring get_domanda ( void ) const { return _domanda.get_stringa (); }
	inline void get_domanda ( Glib::RefPtr<Gtk::TextBuffer> txtbfr ) const { _domanda.get_testo ( txtbfr ); }
	inline void set_risposta ( const Glib::ustring &str, unsigned char index );
	inline void set_risposta ( const char *cstr, unsigned char index );
	inline void set_risposta ( Glib::RefPtr<Gtk::TextBuffer> txtbfr, unsigned char index );
	inline void append_risposta ( const Glib::ustring &str, unsigned char index );
	inline void set_tags_risposta ( const list<Glib::ustring::size_type> &ttl, unsigned char index );
	inline Glib::ustring get_risposta ( unsigned char index ) const;
	inline void get_risposta ( unsigned char index, Glib::RefPtr<Gtk::TextBuffer> txtbfr ) const;
	inline void set_corretta ( const Glib::ustring &str );
	inline void set_corretta ( const char *cstr );
	inline void set_corretta ( char ch ) { _corretta = ch; }
	inline char get_corretta ( void ) const { return _corretta; }
	inline void set_argomento ( const Glib::ustring &str ) { _argomento.assign ( str ); }
	inline void set_argomento ( const char *cstr );
	inline Glib::ustring get_argomento ( void ) const { return _argomento; }
	inline void set_appunti ( const Glib::ustring &str ) { _appunti.set_stringa ( str ); }
	inline void set_appunti ( const char *cstr );
	inline void set_appunti ( Glib::RefPtr<Gtk::TextBuffer> txtbfr ) { _appunti.set_testo ( txtbfr ); }
	inline void append_appunti ( const Glib::ustring &str ) { _appunti.append_stringa ( str ); }
	inline void set_tags_appunti ( const list<Glib::ustring::size_type> &ttl ) { _appunti.set_tags ( ttl ); }
	inline Glib::ustring get_appunti ( void ) const { return _appunti.get_stringa (); }
	inline void get_appunti ( Glib::RefPtr<Gtk::TextBuffer> txtbfr ) const { _appunti.get_testo ( txtbfr); }
	inline void set_risposte ( unsigned short val ) { _risposte = val; }
	inline unsigned short get_risposte ( void ) const { return _risposte; }
	inline void set_corrette ( unsigned short val ) { _corrette = val; }
	inline unsigned short get_corrette ( void ) const { return _corrette; }
	inline void set_ultima ( bool flag ) { _ultimaIsCorretta = flag; }
	inline bool get_ultima ( void ) const { return _ultimaIsCorretta; }
	inline void set_tempo_totale ( time_t sec ) { _tempoTotale = sec; }
	inline time_t get_tempo_totale ( void ) const { return _tempoTotale; }
	inline void set_ultimo_tempo ( time_t sec ) { _ultimoTempo = sec; }
	inline time_t get_ultimo_tempo ( void ) const { return _ultimoTempo; }
	inline void set_is_duplicato ( bool flag ) { _duplicato = flag; }
	inline bool get_is_duplicato ( void ) { return _duplicato; }
	inline void reset ( void );
	inline void test ( bool risultato, int tempo );
	inline void cleanup ( void );
	inline Glib::ustring parse_domanda ( set<Glib::ustring>* parole ) const;
	inline Glib::ustring get_domanda_xml ( void ) const;
	inline unsigned char numero_risposte ( void ) const { return _numeroRisposte; }

protected:
	inline bool shouldnt_ignore ( Glib::ustring &str ) const;
private:
	static const unsigned char _numeroRisposte = 4;
	Glib::ustring _codice;
	TagText _domanda;
	map<unsigned char, TagText> _risposta;
	char _corretta;
	Glib::ustring _argomento;
	TagText _appunti;
	unsigned short _risposte;
	unsigned short _corrette;
	bool _ultimaIsCorretta;
	time_t _tempoTotale;
	time_t _ultimoTempo;
	bool _duplicato;

};

inline Domanda::
Domanda ( const Domanda &quaestio ) :
	_codice ( quaestio._codice ), _domanda ( quaestio._domanda ), _corretta ( quaestio._corretta ), _argomento ( quaestio._argomento ), _appunti ( quaestio._appunti ), _risposte ( quaestio._risposte ), _corrette ( quaestio._corrette ), _ultimaIsCorretta ( quaestio._ultimaIsCorretta ), _tempoTotale ( quaestio._tempoTotale ), _ultimoTempo ( quaestio._ultimoTempo ), _duplicato ( quaestio._duplicato )
{
	unsigned char i = 0;
	map<unsigned char, TagText>::const_iterator iter = quaestio._risposta.begin();
	map<unsigned char, TagText>::const_iterator end = quaestio._risposta.end();
	while ( iter != end )
	{
		_risposta.insert ( pair<unsigned char, TagText> (i, (*iter).second ) );
		i++;
		iter++;
	}
}

inline Domanda& Domanda::
operator=( const Domanda &quaestio )
{
	if ( this != &quaestio )
	{
		_codice = quaestio._codice;
		_domanda = quaestio._domanda;
		_risposta.clear();
		unsigned char i = 0;
		map<unsigned char, TagText>::const_iterator iter = quaestio._risposta.begin();
		map<unsigned char, TagText>::const_iterator end = quaestio._risposta.end();
		while ( iter != end )
		{
			_risposta.insert ( pair<unsigned char, TagText> (i, (*iter).second ) );
			i++;
			iter++;
		}
		_corretta = quaestio._corretta;
		_argomento = quaestio._argomento;
		_appunti = quaestio._appunti;
		_risposte = quaestio._risposte;
		_corrette = quaestio._corrette;
		_ultimaIsCorretta = quaestio._ultimaIsCorretta;
		_tempoTotale = quaestio._tempoTotale;
		_ultimoTempo = quaestio._ultimoTempo;
		_duplicato = quaestio._duplicato;
	}
	return *this;
}

inline bool Domanda::
operator<( const Domanda &quaestio ) const
{
	if ( _codice.raw () < quaestio._codice.raw () )
		return true;
	else
		return false;
}

inline bool Domanda::
operator==( const Domanda &quaestio ) const
{
	if ( _codice.raw () == quaestio._codice.raw () )
		return true;
	else
		return false;
}

inline void Domanda::
set_codice ( const char *cstr )
{
	if ( cstr )
		_codice.assign ( cstr );
}

inline void Domanda::
set_domanda ( const char *cstr )
{
	if ( cstr )
		_domanda.set_stringa ( cstr );
}

inline void Domanda::
set_risposta ( const Glib::ustring &str, unsigned char index )
{
	assert ( index >= 0 && index < _numeroRisposte );
	_risposta[index].set_stringa ( str );
}

inline void Domanda::
set_risposta ( const char *cstr, unsigned char index )
{
	assert ( index >= 0 && index < _numeroRisposte );
	if ( cstr )
		_risposta[index].set_stringa ( cstr );
}

inline void Domanda::
set_risposta ( Glib::RefPtr<Gtk::TextBuffer> txtbfr, unsigned char index )
{
	assert ( index >= 0 && index < _numeroRisposte );
	_risposta[index].set_testo ( txtbfr );
}

inline void Domanda::
append_risposta ( const Glib::ustring &str, unsigned char index )
{
	assert ( index >= 0 && index < _numeroRisposte );
	_risposta[index].append_stringa ( str );
}

inline void Domanda::
set_tags_risposta ( const list<Glib::ustring::size_type> &ttl, unsigned char index )
{
	assert ( index >= 0 && index < _numeroRisposte );
	_risposta[index].set_tags ( ttl );
}

inline Glib::ustring Domanda::
get_risposta ( unsigned char index ) const
{
	assert ( index >= 0 && index < _numeroRisposte );
	map<unsigned char, TagText>::const_iterator iter = _risposta.begin();
	map<unsigned char, TagText>::const_iterator end = _risposta.end();
	while ( iter != end )
	{
		if ( (*iter).first == index )
			break;
		iter++;
	}
	return (*iter).second.get_stringa();
}

inline void Domanda::
get_risposta ( unsigned char index, Glib::RefPtr<Gtk::TextBuffer> txtbfr ) const
{
	assert ( index >= 0 && index < _numeroRisposte );
	map<unsigned char, TagText>::const_iterator iter = _risposta.begin();
	map<unsigned char, TagText>::const_iterator end = _risposta.end();
	while ( iter != end )
	{
		if ( (*iter).first == index )
			break;
		iter++;
	}
	(*iter).second.get_testo ( txtbfr );
}

inline void Domanda::
set_corretta ( const Glib::ustring &str )
{
	switch ( str[0] )
	{
		case 'a':
			_corretta = 0;
			break;
		case 'b':
			_corretta = 1;
			break;
		case 'c':
			_corretta = 2;
			break;
		case 'd':
			_corretta = 3;
			break;
	}
}

inline void Domanda::
set_corretta ( const char *cstr )
{
	switch ( *cstr )
	{
		case 'a':
			_corretta = 0;
			break;
		case 'b':
			_corretta = 1;
			break;
		case 'c':
			_corretta = 2;
			break;
		case 'd':
			_corretta = 3;
			break;
	}
}

inline void Domanda::
set_argomento ( const char *cstr )
{
	if ( cstr )
		_argomento.assign ( cstr );
}

inline void Domanda::
set_appunti ( const char *cstr )
{
	if ( cstr )
		_appunti.set_stringa ( cstr );
}

inline void Domanda::
reset ( void )
{
	_risposte = 0;
	_corrette = 0;
	_ultimaIsCorretta = false;
	_tempoTotale = 0;
	_ultimoTempo = 0;
}

inline void Domanda::
test ( bool risultato, int tempo )
{
	_risposte++;
	if ( risultato )
		_corrette++;
	_ultimaIsCorretta = risultato;
	_tempoTotale += tempo;
	_ultimoTempo = tempo;
}

inline void Domanda::
cleanup ( void )
{
	_codice.assign ( "" );
	_domanda.cleanup();
	map<unsigned char, TagText>::iterator iter = _risposta.begin();
	map<unsigned char, TagText>::iterator end = _risposta.end();
	while ( iter != end )
	{
		iter->second.cleanup();
		iter++;
	}
	set_corretta ( -1 );
	_argomento.assign ( "" );
	_appunti.cleanup();
	reset();
}

inline Glib::ustring Domanda::
parse_domanda ( set<Glib::ustring>* parole ) const
{
	Glib::ustring str = get_domanda ();
	Glib::ustring::size_type inizio = 0;
	Glib::ustring::size_type fine = str.size ();
	Glib::ustring::size_type pos = str.find ( Glib::ustring ( " " ), 0 );
	while ( pos != Glib::ustring::npos && pos > inizio )
	{
		Glib::ustring parola = str.substr ( inizio, pos - inizio ).lowercase ();
		if ( !parole->count ( parola ) && shouldnt_ignore ( parola ) )
			parole->insert ( parola );
		inizio = pos + 1;
		if ( inizio < fine - 1)
			pos = str.find ( Glib::ustring ( " " ), inizio );
		else
			break;
	}
	if ( inizio < fine - 1 )
	{
		Glib::ustring parola = str.substr ( inizio, fine - inizio - 1 ).lowercase ();
		if ( !parole->count ( parola ) && shouldnt_ignore ( parola ) )
			parole->insert ( parola );
	}
	return str;
}

inline bool Domanda::
shouldnt_ignore ( Glib::ustring &str ) const
{
	bool importante = true;
	for ( short i = 0; i < APOSTROFI; i++ )
	{
		char n = apostrofi[i].size();
		if ( str.size() >= n + 1 )
		{
			Glib::ustring apox = str.substr ( n, 1 );
			if ( apox == Glib::ustring ( "\'" ) || apox == Glib::ustring ( "’" ) )
			{
				Glib::ustring prefix = str.substr ( 0, n );
				if ( prefix == apostrofi[i] )
				{
					str.erase ( 0, n + 1 );
					break;
				}
			}
		}
	}
	Glib::ustring punto = str.substr ( str.size() - 1, 1 );
	for ( short i = 0; i < PUNTI; i++ )
	{
		if ( punto == punti[i] )
		{
			str.erase ( str.size() - 1, 1 );
			break;
		}
	}
	for ( short i = 0; i < DA_IGNORARE; i++ )
	{
		if ( daIgnorare[i] == str )
		{
			importante = false;
			break;
		}
	}
	return importante;
}

inline Glib::ustring Domanda::
get_domanda_xml ( void ) const
{
	Glib::ustring str;
	struct tm *pTempo;
	str.append ( Glib::ustring ( "\t<domanda>\n" ) );
	str.append ( Glib::ustring ( "\t\t<codex>" + TagText::get_string_with_entity_references ( _codice ) + "</codex>\n" ) );
	str.append ( Glib::ustring ( "\t\t<quaestio>\n" ) + _domanda.get_tag_text_xml () + Glib::ustring ( "\t\t</quaestio>\n" ) );
	map<unsigned char, TagText>::const_iterator iter = _risposta.begin();
	map<unsigned char, TagText>::const_iterator end = _risposta.end();
	while ( iter != end )
	{
		str.append ( Glib::ustring ( "\t\t<responsio>\n" ) + (*iter).second.get_tag_text_xml () + Glib::ustring ( "\t\t</responsio>\n" ) );
		iter++;
	}
	str.append ( Glib::ustring ( "\t\t<diligens>" + Glib::ustring::format ( static_cast<int>( _corretta ) ) + "</diligens>\n" ) );
	if ( _argomento != Glib::ustring ( "" ) )
		str.append ( Glib::ustring ( "\t\t<argumentum>" + TagText::get_string_with_entity_references ( _argomento ) + "</argumentum>\n" ) );
	if ( _appunti.get_stringa () != Glib::ustring ( "" ) )
		str.append ( Glib::ustring ( "\t\t<nota>\n" ) + _appunti.get_tag_text_xml () + Glib::ustring ( "\t\t</nota>\n" ) );
	if ( _risposte > 0 )
	{
		str.append ( Glib::ustring ( "\t\t<responsiones>" + Glib::ustring::format ( _risposte ) + "</responsiones>\n" ) );
		str.append ( Glib::ustring ( "\t\t<diligentes>" + Glib::ustring::format ( _corrette ) + "</diligentes>\n" ) );
		str.append ( Glib::ustring ( "\t\t<extremus>" + Glib::ustring::format ( _ultimaIsCorretta ) + "</extremus>\n" ) );
		if ( _tempoTotale > 0 )
		{
			pTempo = localtime ( &_tempoTotale );
			str.append ( Glib::ustring::compose ( "\t\t<tempus_summa>P%1Y%2M%3DT%4H%5M%6S</tempus_summa>\n", Glib::ustring::format ( pTempo->tm_year - 70 ), Glib::ustring::format ( pTempo->tm_mon ), Glib::ustring::format ( pTempo->tm_mday - 1 ), Glib::ustring::format ( pTempo->tm_hour - 1 ), Glib::ustring::format ( pTempo->tm_min ), Glib::ustring::format ( pTempo->tm_sec ) ) );
		}
		if ( _ultimoTempo > 0 )
		{
			pTempo = localtime ( &_ultimoTempo );
			str.append ( Glib::ustring::compose ( "\t\t<tempus_extremus>P%1Y%2M%3DT%4H%5M%6S</tempus_extremus>\n", Glib::ustring::format ( pTempo->tm_year - 70 ), Glib::ustring::format ( pTempo->tm_mon ), Glib::ustring::format ( pTempo->tm_mday - 1 ), Glib::ustring::format ( pTempo->tm_hour - 1 ), Glib::ustring::format ( pTempo->tm_min ), Glib::ustring::format ( pTempo->tm_sec ) ) );
		}
	}
	str.append ( Glib::ustring ( "\t</domanda>\n" ) );
	return str;
}

#endif // _DOMANDA_H_
